/*
 *  Copyright 2011 Alexey Andreev.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package org.xthl.core;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import org.nop.core.BeanScope;
import org.nop.core.ManagedBean;
import org.nop.sql.DataManager;
import org.nop.sql.DataResult;
import org.nop.sql.ExprBuilder;
import org.nop.sql.QueryBuilder;
import org.nop.util.Injected;
import org.xthl.core.api.RestrictedAccessException;
import org.xthl.core.api.Role;
import org.xthl.core.api.RoleLogic;
import org.xthl.core.api.RoleTypeFilter;
import org.xthl.core.api.UpdateRoleCommand;
import org.xthl.core.data.DefaultRoleSource;
import org.xthl.core.data.PermissionSource;
import org.xthl.core.data.RolePermissionSource;
import org.xthl.core.data.RoleSource;

/**
 *
 * @author Alexey Andreev
 */
@ManagedBean(iface = RoleLogic.class, scope = BeanScope.REQUEST)
public class RoleLogicImpl implements RoleLogic {
    private DataManager dataManager;
    private QueryBuilder qb;
    
    @Injected
    public RoleLogicImpl(DataManager dataManager) {
        this.dataManager = dataManager;
        this.qb = dataManager.getQueryBuilder();
    }

    private Role getRole(DataResult result) {
        Role role = new Role();
        role.setId(result.getInt(1));
        role.setName(result.getString(2));
        role.setSystem(result.getBoolean(3));
        return role;
    }
    
    @Override
    public List<Role> getRoles(RoleTypeFilter typeFilter) throws RestrictedAccessException {
        RoleSource role = qb.get(RoleSource.class);
        ExprBuilder filter = qb.getTrue();
        switch (typeFilter) {
            case SYSTEM:
                filter = role.system().isTrue();
                break;
            case USER:
                filter = role.system().isFalse();
                break;
        }
        DataResult result = dataManager.exec(qb.with(role)
                .filter(filter)
                .sortAsc(role.name())
                .fetch(role.id(), role.name(), role.system()));
        List<Role> dtos = new ArrayList<Role>();
        while (result.next()) {
            dtos.add(getRole(result));
        }
        return dtos;
    }

    @Override
    public Role getRole(int id) {
        RoleSource role = qb.get(RoleSource.class);
        DataResult result = dataManager.exec(qb.with(role)
                .filter(role.id().eq(id))
                .fetch(role.id(), role.name(), role.system()));
        return result.next() ? getRole(result) : null;
    }

    @Override
    public Role createRole(UpdateRoleCommand command) {
        RoleSource role = qb.get(RoleSource.class);
        Random random = new Random();
        int cnt = 0;
        int id;
        while (true) {
            id = 100000000 + random.nextInt(900000000); 
            boolean ok = dataManager.exec(qb.insertInto(role)
                    .field(role.id(), id)
                    .field(role.name(), command.getName())
                    .field(role.system(), command.isSystem()));
            if (ok) {
                break;
            }
            ++cnt;
            if (cnt > 20) {
                throw new RuntimeException("Could not create role");
            }
        }
        Role dto = new Role();
        dto.setId(id);
        dto.setName(command.getName());
        dto.setSystem(command.isSystem());
        return dto;
    }

    @Override
    public void setRole(int id, UpdateRoleCommand command) {
        RoleSource role = qb.get(RoleSource.class);
        dataManager.exec(qb.with(role)
                .filter(role.id().eq(id))
                .set(role.name(), command.getName())
                .set(role.system(), command.isSystem()));
    }

    @Override
    public void setRolePermissions(int id, List<String> permissions)
            throws RestrictedAccessException {
        RolePermissionSource rolePerm = qb.get(RolePermissionSource.class);
        PermissionSource perm = qb.get(PermissionSource.class);
        dataManager.exec(qb.with(rolePerm)
                .filter(rolePerm.roleId().eq(id))
                .delete());
        if (!permissions.isEmpty()) {
            dataManager.exec(qb.with(perm)
                    .filter(perm.name().inStrings(permissions))
                    .insertInto(rolePerm)
                    .field(rolePerm.roleId(), qb.wrap(id))
                    .field(rolePerm.permissionId(), perm.id()));
        }
    }

    @Override
    public List<Integer> getDefaultRoleIds() throws RestrictedAccessException {
        DefaultRoleSource role = qb.get(DefaultRoleSource.class);
        DataResult result = dataManager.exec(qb.with(role)
                .filter(role.type().eq("default"))
                .fetch(role.roleId()));
        List<Integer> ids = new ArrayList<Integer>();
        while (result.next()) {
            ids.add(result.getInt(1));
        }
        return ids;
    }

    @Override
    public void setDefaultRoleIds(Set<Integer> roleIds) throws RestrictedAccessException {
        DefaultRoleSource role = qb.get(DefaultRoleSource.class);
        dataManager.exec(qb.with(role).filter(role.type().eq("default")).delete());
        for (int roleId : roleIds) {
            dataManager.exec(qb.insertInto(role)
                    .field(role.roleId(), roleId)
                    .field(role.type(), "default"));
        }
    }

    @Override
    public List<Integer> getGuestRoleIds() throws RestrictedAccessException {
        DefaultRoleSource role = qb.get(DefaultRoleSource.class);
        DataResult result = dataManager.exec(qb.with(role)
                .filter(role.type().eq("guest"))
                .fetch(role.roleId()));
        List<Integer> ids = new ArrayList<Integer>();
        while (result.next()) {
            ids.add(result.getInt(1));
        }
        return ids;
    }

    @Override
    public void setGuestRoleIds(Set<Integer> roleIds) throws RestrictedAccessException {
        DefaultRoleSource role = qb.get(DefaultRoleSource.class);
        dataManager.exec(qb.with(role).filter(role.type().eq("guest")).delete());
        for (int roleId : roleIds) {
            dataManager.exec(qb.insertInto(role)
                    .field(role.roleId(), roleId)
                    .field(role.type(), "guest"));
        }
    }

    @Override
    public List<String> getRolePermissions(int id) throws RestrictedAccessException {
        RolePermissionSource rolePerm = qb.get(RolePermissionSource.class);
        PermissionSource perm = qb.get(PermissionSource.class);
        DataResult result = dataManager.exec(qb.with(rolePerm)
                .join(perm, perm.id().eq(rolePerm.permissionId()))
                .filter(rolePerm.roleId().eq(id))
                .sortAsc(perm.name())
                .fetch(perm.name()));
        List<String> permissions = new ArrayList<String>();
        while (result.next()) {
            permissions.add(result.getString(1));
        }
        return permissions;
    }

    @Override
    public List<String> getPermissions() {
        PermissionSource perm = qb.get(PermissionSource.class);
        DataResult result = dataManager.exec(qb.with(perm)
                .sortAsc(perm.name())
                .fetch(perm.name()));
        List<String> permissions = new ArrayList<String>();
        while (result.next()) {
            permissions.add(result.getString(1));
        }
        return permissions;
    }
}
